/**
 * Copyright � 2012 GestureSoft. ALL RIGHTS RESERVED.
 * This file contains proprietary and GestureSoft CONFIDENTIAL Information.
 * Use, disclosure or reproduction is prohibited.
 * 
 * @fileName TwitterService.as
 * @creationDate Aug 8, 2012
 * @author 
 * @version 1.0
 */
package com.gesturesoft.imultimax.socialize.twitter
{
	import com.gesturesoft.imultimax.socialize.destroytoday.net.XMLLoader;
	import com.gesturesoft.imultimax.socialize.destroytoday.twitteraspirin.TwitterAspirin;
	import com.gesturesoft.imultimax.socialize.destroytoday.twitteraspirin.vo.OAuthTokenVO;
	import com.gesturesoft.imultimax.socialize.destroytoday.twitteraspirin.vo.StatusVO;
	
	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
	import mx.rpc.http.HTTPService;
	
	import com.gesturesoft.imultimax.socialize.org.gesturesoft.oauth.IOAuthSignatureMethod;
	import com.gesturesoft.imultimax.socialize.org.gesturesoft.oauth.OAuthConsumer;
	import com.gesturesoft.imultimax.socialize.org.gesturesoft.oauth.OAuthRequest;
	import com.gesturesoft.imultimax.socialize.org.gesturesoft.oauth.OAuthSignatureMethod_HMAC_SHA1;
	import com.gesturesoft.imultimax.socialize.org.gesturesoft.oauth.OAuthToken;
	import com.gesturesoft.imultimax.socialize.org.osflash.signals.Signal;
	
	public class TwitterService
	{
		/** 
		 * A Consumer Key is given to your application after 
		 * it's creation.  You can change this value on dev.twitter.com
		 **/
		private var __consumerKey:String;
		/**
		 * A Consumer Secret is also assigned to your
		 * application upon creation, and is changeable.
		 **/
		private var __consumerSecret:String;
		
		// URLs needed for requests
		private var __authTokenURL:String;
		private var __accessTokenURL:String;
		private var __authorizeURL:String;
		
		// Your app's name on dev.twitter.com
		private var __applicationName:String;
		// Current urlLoader callback
		private var urlLoaderCallback:Function;
		// Primary loader for authentication calls
		private var urlLoader:URLLoader;
		// VO for key and secret
		private var consumer:OAuthConsumer;
		// Twitter requires that all OAuth requests be signed using the HMAC-SHA1 algorithm
		private var signatureMethod:IOAuthSignatureMethod = new OAuthSignatureMethod_HMAC_SHA1();
		private var reqVO:OAuthTokenVO;
		// Dynamically-generated by oAuthRequest
		public var loginURL:String;
		// Needed by OAuthRequest and TwitterAspirin
		public var authToken:OAuthToken;
		// ID returned from a successful access code call
		public var userID:Number;
		// Screen name returned from a successful access code call
		public var screenName:String;
		
		/**
		 * Signals for application flow.  If a class listens to 
		 * any of these, the method passed as the listener will
		 * be triggered and an optional argument will be passed.
		 **/
		public var loginURLReady:Signal = new Signal();
		public var accessTokenReceived:Signal = new Signal();
		public var failure:Signal = new Signal();
		public var Connectionfailure:Signal = new Signal();
		public var tweetSucces:Signal = new Signal();
		public var updatedStatus:Signal = new Signal();
		
		// Implements most generic Twitter functionality
		private var twitterAspirin:TwitterAspirin;
		
		private var httpService:HTTPService = new HTTPService();
		
		public function TwitterService()
		{
			super();
		}

		/**
		 * Begins the process of generating a request token.
		 * Creates the consumer.  Creates the URLLoader.
		 * Establishes any listeners needed for Signals. 
		 * 
		 */
		public function start():void
		{
			// TwitterAspirin will be used for routine calls
			if (this.twitterAspirin == null) {
				this.twitterAspirin = new TwitterAspirin(this.consumerKey,this.consumerSecret);
			} 
			
			this.consumer = new OAuthConsumer(this.consumerKey,this.consumerSecret);
			
			if (urlLoader == null) {
				urlLoader = new URLLoader();
			}
			
			// Set this initially; from now on we won't have to
			urlLoaderCallback = onGetAuthTokenComplete;
			
			// Prepare listeners for the request
			initURLLoaderListeners(onGetAuthTokenComplete);
			
			// Create our request object
			var oauthRequest:OAuthRequest = new OAuthRequest(
				URLRequestMethod.GET,
				this.authTokenURL, 
				null,
				consumer);
			
			// Build our request string
			var signedURL:String = oauthRequest.buildRequest(
				signatureMethod,
				OAuthRequest.RESULT_TYPE_URL_STRING, 
				"");
			
			var urlRequest:URLRequest = new URLRequest(signedURL);
			urlRequest.method = URLRequestMethod.GET;
			
			// Load the URL
			try {
				urlLoader.load(urlRequest);
			} catch(err:Error) {
				failure.dispatch(err.message);
			}
		}
		
		/**
		 * 
		 * @param code - PIN sent by the user after a successful authorization flow
		 * 
		 */
		public function getAccessToken(code:String):void
		{
			urlLoader.removeEventListener(Event.COMPLETE, onGetAuthTokenComplete);
			urlLoader.addEventListener(Event.COMPLETE, onGetAccessTokenComplete);
			
			var oauthRequest:OAuthRequest = new OAuthRequest(URLRequestMethod.GET,
				this.accessTokenURL, 
				null,
				consumer,
				authToken);
			
			var url:String = oauthRequest.buildRequest(signatureMethod,
				OAuthRequest.RESULT_TYPE_URL_STRING, 
				"");
			
			try {
				urlLoader.load(new URLRequest(url));
			} catch(err:Error) {
				failure.dispatch(err);
			}
		}
		
		/**
		 * 
		 * @param tweetText - Text of the tweet
		 * 
		 */
		public function sendTweet(tweetText:String):void
		{
			this.twitterAspirin.updatedStatus.removeAll();
			this.twitterAspirin.updatedStatus.add(onUpdatedStatus);
			
			if (tweetText.length<140) {
				this.twitterAspirin.updateStatus(reqVO,tweetText);
			} else {
				this.failure.dispatch("Tweet is too long");
			}
		}
		
		/**
		 * 
		 * @param loadr - the XMLLoader that received the status message
		 * @param status - the status message itself
		 * 
		 */
		private function onUpdatedStatus(loadr:XMLLoader, status:StatusVO):void
		{
			tweetSucces.dispatch(status);
		}
		
		/**
		 * 
		 * @param event - status returned from server
		 * 
		 */
		private function onGetAuthTokenComplete(event:Event):void
		{
			parseTokenRequest(String(urlLoader.data));
			
			this.loginURLReady.dispatch();
		}
		
		/**
		 * 
		 * @param result - string returned from authentication flow step 1
		 * 
		 */
		private function parseTokenRequest(result:String):void
		{
			var aResult:Array = result.split("&");
			var split:Array;
			var oResult:Object = new Object();
			for each(var item:String in aResult) {
				split = item.split("=");
				oResult[split[0]] = split[1];
			}
			
			this.authToken = new OAuthToken(oResult.oauth_token, oResult.oauth_token_secret);
			
			var url:String = "";
			url += this.authorizeURL;
			url += "?oauth_callback=oob";
			url += "&application_name="+this.applicationName;
			url += "&oauth_token=" + oResult.oauth_token;
			url += "&oauth_consumer_key=" + this.consumer.key;
			
			loginURL = unescape(url);
		}
		
		/**
		 * 
		 * @param event - Handles HTTPResponseStatusEvents from urlLoader POST and GET requests
		 * 
		 */
		private function onHTTPResponseStatus(event:HTTPStatusEvent):void
		{
			if(event.status == 0)
				Connectionfailure.dispatch();
			trace("TwitterService::onHTTPResponseStatus: " + event.status);
		}
		
		/**
		 * 
		 * @param event - Handles HTTPStatusEvents from urlLoader POST and GET requests
		 * 
		 */
		private function onHTTPStatus(event:HTTPStatusEvent):void
		{
			if(event.status == 0)
				Connectionfailure.dispatch();
			trace("TwitterService::onHTTPStatus: " + event.status);
		}
		
		/**
		 * 
		 * @param event - handles i/o or security errors
		 * 
		 */
		private function onError(event:ErrorEvent):void
		{
			trace("TwitterService::onError: " + event);
		}
		
		/**
		 * 
		 * @param event - resulting data from successful access token creation
		 * 
		 */
		private function onGetAccessTokenComplete(event:Event):void
		{
			trace("TwitterService::onGetAccessTokenComplete, data: " + urlLoader.data);
			
			var result:String = urlLoader.data as String;
			var aResult:Array = result.split("&");
			var split:Array;
			var oResult:Object = new Object();
			for each(var item:String in aResult) {
				split = item.split("=");
				oResult[split[0]] = split[1];
			}
			
			this.authToken = new OAuthToken(oResult.oauth_token, oResult.oauth_token_secret);
			
			if (reqVO == null) {
				reqVO = new OAuthTokenVO();
				reqVO.key = this.authToken.key;
				reqVO.secret = this.authToken.secret;
			}
			
			this.userID = parseInt(oResult.user_id);
			this.screenName = oResult.screen_name;
			
			this.accessTokenReceived.dispatch();
		}
		
		/**
		 * 
		 * @param callBack - new function to be used as this method's callback
		 * 
		 */
		private function initURLLoaderListeners(callBack:Function):void
		{
			urlLoader.removeEventListener(Event.COMPLETE, urlLoaderCallback);
			urlLoader.removeEventListener(HTTPStatusEvent.HTTP_RESPONSE_STATUS, onHTTPResponseStatus);
			urlLoader.removeEventListener(HTTPStatusEvent.HTTP_STATUS, onHTTPStatus);
			urlLoader.removeEventListener(IOErrorEvent.IO_ERROR, onError);
			urlLoader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
			
			urlLoader.addEventListener(Event.COMPLETE, callBack);
			urlLoader.addEventListener(HTTPStatusEvent.HTTP_RESPONSE_STATUS, onHTTPResponseStatus);
			urlLoader.addEventListener(HTTPStatusEvent.HTTP_STATUS, onHTTPStatus);
			urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onError);
			urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
			
			this.urlLoaderCallback = callBack;
		}
		
		public function get consumerKey():String
		{
			return __consumerKey;
		}
		
		public function set consumerKey(val:String):void
		{
			__consumerKey = val;
		}
		
		public function get consumerSecret():String
		{
			return __consumerSecret;
		}
		
		public function set consumerSecret(val:String):void
		{
			__consumerSecret = val;
		}
		
		public function get authTokenURL():String
		{
			return __authTokenURL;
		}
		
		public function set authTokenURL(val:String):void
		{
			__authTokenURL = val;
		}
		
		public function get accessTokenURL():String
		{
			return __accessTokenURL;
		}
		
		public function set accessTokenURL(val:String):void
		{
			__accessTokenURL = val;
		}
		
		public function get authorizeURL():String
		{
			return __authorizeURL;
		}
		
		public function set authorizeURL(val:String):void
		{
			__authorizeURL = val;
		}
		
		public function get applicationName():String
		{
			return __applicationName;
		}
		
		public function set applicationName(val:String):void
		{
			__applicationName = val;
		}
	}
}